home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pico / attach.c < prev    next >
C/C++ Source or Header  |  1996-04-26  |  25KB  |  1,025 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: attach.c,v 4.35 1996/04/26 16:15:01 hubert Exp $";
  3. #endif
  4. /*
  5.  * Program:    Routines to support attachments in the Pine composer 
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1996 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  */
  30. #include <stdio.h>
  31. #include <ctype.h>
  32. #include <math.h>
  33. #include "osdep.h"
  34. #include "pico.h"
  35. #include "estruct.h"
  36. #include "edef.h"
  37. #include "efunc.h"
  38.  
  39. #ifdef    ATTACHMENTS
  40.  
  41.  
  42. #ifdef    ANSI
  43.     int    ParseAttach(struct hdr_line **,int *,char *,char *,char *,int *);
  44.     PATMT *NewAttach(char *, long, char *);
  45.     void   ZotAttach(struct pico_atmt *);
  46.     int    sinserts(char *, int, char *, int);
  47.     int       AttachUpload(char *, char *);
  48.     int       AttachCancel(char *);
  49. #else
  50.     int    ParseAttach();
  51.     PATMT *NewAttach();
  52.     void   ZotAttach();
  53.     int    sinserts();
  54.     int       AttachUpload();
  55.     int       AttachCancel();
  56. #endif
  57.  
  58.  
  59. /* 
  60.  * max number of attachments
  61.  */
  62. #define    MAXATCH    64
  63. #define    HIBIT_WARN    "Only ASCII characters allowed in attachment comments"
  64.  
  65.  
  66. /*
  67.  * AskAttach - ask for attachment fields and build resulting structure
  68.  *             return pointer to that struct if OK, NULL otherwise
  69.  */
  70. AskAttach(fn, sz, cmnt)
  71. char *fn, *sz, *cmnt;
  72. {
  73.     int        i, status, upload = 0;
  74.     long    l = 0;
  75.     char    bfn[NFILEN];
  76.  
  77.     i = 2;
  78.     fn[0] = '\0';
  79.     sz[0] = '\0';
  80.     cmnt[0] = '\0';
  81.  
  82.     while(i){
  83.     if(i == 2){
  84.         EXTRAKEYS menu_attach[3];
  85.         int          n;
  86.  
  87.         menu_attach[n = 0].name  = "^T";
  88.         menu_attach[n].label     = "To Files";
  89.         menu_attach[n].key         = (CTRL|'T');
  90. #if    !defined(DOS) && !defined(MAC)
  91.         if(Pmaster && Pmaster->upload){
  92.         /*
  93.          * The Plan: ^R prompts for uploaded file's name which
  94.          * is passed to the defined upload command when the user
  95.          * hits Return to confirm the name.
  96.          * NOTE: this is different than upload into message
  97.          * text in which case the uploaded name isn't useful so
  98.          * a temp file is ok (be sure to fix the file mode).
  99.          */
  100.         menu_attach[++n].name = "^Y";
  101.         menu_attach[n].key    = (CTRL|'Y');
  102.         menu_attach[n].label  = upload ? "Read File" : "RcvUpload";
  103.         }
  104. #endif
  105.  
  106.         menu_attach[++n].name  = NULL;
  107.         KS_OSDATASET(&menu_attach[0], KS_NONE);
  108.         status = mlreply(upload ? "Name to give uploaded attachment: "
  109.                     : "File to attach: ",
  110.                  fn, NLINE, QNORML, menu_attach);
  111.     }
  112.     else
  113.       status = mlreply("Attachment comment: ", cmnt, NLINE, QNORML, NULL);
  114.  
  115.     switch(status){
  116.       case HELPCH:
  117.         emlwrite("No Attachment %s help yet!",
  118.              (i == 2) ? "file" : "comment");
  119. /* remove break and sleep when help text done to force redraw */     
  120.         sleep(3);
  121.         break;
  122.  
  123.       case (CTRL|'T'):
  124.         if(i != 2){
  125.         (*term.t_beep)();
  126.         break;
  127.         }
  128.  
  129.         *bfn = '\0';
  130.         if(*fn == '\0' || !isdir(fn, NULL))
  131.           strcpy(fn, gmode&MDCURDIR ? "."
  132.                     : (gmode&MDTREE) ? opertree
  133.                              : gethomedir(NULL));
  134.  
  135.         if(FileBrowse(fn, bfn, sz, FB_READ) == 1){
  136.         strcat(fn, S_FILESEP);
  137.         strcat(fn, bfn);
  138.         if(upload && !AttachUpload(fn, sz)){
  139.             i = 2;            /* keep prompting for file */
  140.             sleep(3);            /* problem, show error! */
  141.         }
  142.         else
  143.           i--;                /* go prompt for comment */
  144.         }
  145.         else
  146.           *fn = '\0';
  147.  
  148.         /* fall thru to clean up the screen */
  149.  
  150.       case (CTRL|'L'):
  151.         refresh(FALSE, 1);
  152.         update();
  153.         continue;
  154.  
  155. #if    !defined(DOS) && !defined(MAC)
  156.       case (CTRL|'Y'):            /* upload? */
  157.         if(i == 2)
  158.           upload ^= 1;            /* flip mode */
  159.         else
  160.           (*term.t_beep)();
  161.  
  162.         break;
  163. #endif
  164.  
  165.       case ABORT:
  166.         return(AttachCancel((upload && i == 1) ? fn : NULL));
  167.  
  168.       case TRUE:                /* some comment */
  169.       case FALSE:                /* No comment */
  170.         if(i-- == 2){
  171.         if(upload){
  172.             fixpath(fn, NLINE);        /* names relative to ~ */
  173.             status = AttachUpload(fn, sz);
  174.             refresh(FALSE, 1);
  175.             update();
  176.             if(!status){
  177.             i = 2;            /* keep prompting for file */
  178.             sleep(3);        /* problem, show error! */
  179.             }
  180.         }
  181.         else if(fn[0]){
  182.             if(gmode&MDTREE)
  183.               compresspath(opertree, fn, NLINE);
  184.  
  185.             fixpath(fn, NLINE);        /* names relative to ~ */
  186.             if((gmode&MDTREE) && !in_oper_tree(fn)){
  187.             emlwrite(
  188.             "\007Restricted mode allows attachments from %s only",
  189.             (gmode&MDSCUR) ? "home directory" : opertree);
  190.             return(0);
  191.             }
  192.  
  193.             if((status=fexist(fn, "r", &l)) != FIOSUC){
  194.             fioperr(status, fn);    /* file DOESN'T exist! */
  195.             return(0);
  196.             }
  197.             strcpy(sz, prettysz(l));
  198.         }
  199.         else
  200.           return(AttachCancel((upload && i == 1) ? fn : NULL));
  201.         }
  202.         else{
  203.         mlerase();
  204.         return(1);            /* mission accomplished! */
  205.         }
  206.  
  207.         break;
  208.       default:
  209.         break;
  210.     }
  211.     }
  212. }
  213.  
  214.  
  215. /*
  216.  * AttachUpload - Use call back to run the external upload command.
  217.  */
  218. int
  219. AttachUpload(fn, sz)
  220.     char *fn, *sz;
  221. {
  222.     long l;
  223.  
  224.     if(gmode&MDSCUR){
  225.     emlwrite("\007Restricted mode disallows uploaded command", NULL);
  226.     return(0);
  227.     }
  228.  
  229.     if(Pmaster && Pmaster->upload && (*Pmaster->upload)(fn, &l)){
  230.     strcpy(sz, prettysz(l));
  231.     return(1);
  232.     }
  233.  
  234.     return(0);
  235. }
  236.  
  237.  
  238. /*
  239.  * AttachCancel - 
  240.  */
  241. int
  242. AttachCancel(fn)
  243.     char *fn;
  244. {
  245.     emlwrite("Attach cancelled", NULL);
  246.     if(fn && fn[0])
  247.       unlink(fn);                /* blast uploaded file */
  248.  
  249.     return(0);
  250. }
  251.  
  252.  
  253. extern struct headerentry *headents;
  254.  
  255. /*
  256.  * SyncAttach - given a pointer to a linked list of attachment structures,
  257.  *              return with that structure sync'd with what's displayed.
  258.  *              delete any attachments in list of structs that's not on 
  259.  *              the display, and add any that aren't in list but on display.
  260.  */
  261. SyncAttach()
  262. {
  263.     int offset = 0,                /* the offset to begin       */
  264.         rv = 0,
  265.         ki = 0,                    /* number of known attmnts   */
  266.         bi = 0,                    /* build array index         */
  267.         na,                    /* old number of attachmnt   */
  268.         status, i, j, n;
  269.     char file[NLINE],                /* buffers to hold it all    */
  270.          size[32],
  271.          comment[1024];
  272.     struct hdr_line *lp;            /* current line in header    */
  273.     struct headerentry *entry;
  274.     PATMT *tp, *knwn[MAXATCH], *bld[MAXATCH];
  275.  
  276.     for(entry = headents; entry->name != NULL; entry++) {
  277.       if(entry->is_attach)
  278.     break;
  279.     }
  280.  
  281.     if(Pmaster == NULL)
  282.       return(-1);
  283.  
  284.     for(i=0;i<MAXATCH;i++)            /* bug - ever pop this? */
  285.     knwn[i] = bld[i] = NULL;        /* zero out table */
  286.     
  287.     tp = Pmaster->attachments;
  288.     while(tp != NULL){                /* fill table of     */
  289.     knwn[ki++] = tp;            /* known attachments */
  290.     tp = tp->next;
  291.     }
  292.  
  293.     n = 0;
  294.  
  295.     lp = entry->hd_text;
  296.     while(lp != NULL){
  297.     na = ++n;
  298.  
  299.     if(status = ParseAttach(&lp, &offset, file, size, comment, &na))
  300.         rv = (rv < 0) ? rv : status ;    /* remember worst case */
  301.  
  302.     if(*file == '\0'){
  303.         if(n != na && na > 0 && na <= ki && (knwn[na-1]->flags & A_FLIT)){
  304.         bld[bi++] = knwn[na-1];
  305.         knwn[na-1] = NULL;
  306.         }
  307.         continue;
  308.     }
  309.  
  310.     if((gmode&MDTREE) && !in_oper_tree(file))
  311.       /* no attachments outsize ~ in secure mode! */
  312.       continue;
  313.  
  314.     tp = NULL;
  315.     for(i = 0; i < ki; i++){        /* already know about it? */
  316.         /*
  317.          * this is kind of gruesome. what we want to do is keep track
  318.          * of literal attachment entries because they may not be 
  319.          * actual files we can access or that the user can readily 
  320.          * access.
  321.          */
  322.         if(knwn[i]
  323.            && ((!(knwn[i]->flags&A_FLIT)
  324.             && !strcmp(file, knwn[i]->filename))
  325.            || ((knwn[i]->flags&A_FLIT) && i+1 == na))){
  326.         tp = knwn[i];
  327.         knwn[i] = NULL;            /* forget we know about it */
  328.  
  329.         if(status == -1)        /* ignore garbage! */
  330.           break;
  331.  
  332.         if((tp->flags&A_FLIT) && strcmp(file, tp->filename)){
  333.             rv = 1;
  334.             if((j=strlen(file)) > strlen(tp->filename)){
  335.             if((tp->filename = (char *)realloc(tp->filename,
  336.                             sizeof(char)*(j+1))) == NULL){
  337.                 emlwrite("\007Can't realloc filename space",NULL);
  338.                 return(-1);
  339.             }
  340.             }
  341.  
  342.             strcpy(tp->filename, file);
  343.         }
  344.         else if(tp->size && strcmp(tp->size, size)){
  345.             rv = 1;
  346.             if((j=strlen(size)) > strlen(tp->size)){
  347.             if((tp->size=(char *)realloc(tp->size,
  348.                             sizeof(char)*(j+1))) == NULL){
  349.                 emlwrite("\007Can't realloc space for size", NULL);
  350.                 return(-1);
  351.             }
  352.             }
  353.  
  354.             strcpy(tp->size, size);
  355.         }
  356.  
  357.         if(strcmp(tp->description, comment)){    /* new comment */
  358.             rv = 1;
  359.             if((j=strlen(comment)) > strlen(tp->description)){
  360.             if((tp->description=(char *)realloc(tp->description,
  361.                         sizeof(char)*(j+1))) == NULL){
  362.                 emlwrite("\007Can't realloc description", NULL);
  363.                 return(-1);
  364.             }
  365.             }
  366.               
  367.             strcpy(tp->description, comment);
  368.         }
  369.         break;
  370.         }
  371.     }
  372.  
  373.     if(tp){
  374.         bld[bi++] = tp;
  375.     }
  376.     else{
  377.         if(file[0] != '['){
  378.         if((tp = NewAttach(file, atol(size), comment)) == NULL)
  379.           return(-1);
  380.         bld[bi++] = tp;
  381.         }
  382.         else break;
  383.     }
  384.  
  385.     if(status < 0)
  386.       tp->flags |= A_ERR;        /* turn ON error bit */
  387.     else
  388.       tp->flags &= ~(A_ERR);    /* turn OFF error bit */
  389.     }
  390.  
  391.     for(i=0; i < bi; i++)        /* link together newly built list */
  392.     bld[i]->next = bld[i+1];
  393.  
  394.     Pmaster->attachments = bld[0];
  395.  
  396.     for(i = 0; i < ki; i++){        /* kill old/unused references */
  397.  
  398.     if(knwn[i]){
  399.         ZotAttach(knwn[i]);
  400.         free((char *) knwn[i]);
  401.     }
  402.     }
  403.  
  404.     return(rv);
  405. }
  406.  
  407.  
  408.  
  409.  
  410. /*
  411.  * ParseAttach - given a header line and an offset into it, return with 
  412.  *         the three given fields filled in.  Assumes the size of 
  413.  *         the buffers passed is large enough to hold what's there.
  414.  *         Always updates header fields that have changed or are 
  415.  *         fixed.  An error advances offset to next attachment.
  416.  *
  417.  *        returns: 1 if a field changed
  418.  *                       0 nothing changed
  419.  *                      -1 on error
  420.  */
  421. ParseAttach(lp, off, fn, sz, cmnt, no)
  422. struct hdr_line **lp;                /* current header line      */
  423. int  *off;                    /* offset into that line    */
  424. char *fn, *sz, *cmnt;                /* places to return fields  */
  425. int  *no;                    /* attachment number        */
  426. {
  427.     int  j, status,                /* various return codes     */
  428.          rv = 0,                /* return value             */
  429.      orig_offset,
  430.          lbln  = 0,                /* label'd attachment        */
  431.      hibit = 0;
  432.     long l;                    /* attachment length        */
  433.     char tmp[1024],
  434.      c,
  435.      c_lookahead,
  436.         *p,
  437.         *lblsz = NULL,                /* label'd attchmnt's size  */
  438.          number[8];
  439.     register struct hdr_line  *lprev;
  440.     enum {                    /* parse levels             */
  441.     LWS,                    /* leading white space      */
  442.     NUMB,                    /* attachment number        */
  443.     WSN,                    /* white space after number */
  444.     TAG,                    /* attachments tag (fname)  */
  445.     WST,                    /* white space after tag    */
  446.     SIZE,                    /* attachments size         */
  447.     SWS,                    /* white space after size   */
  448.     CMMNT,                    /* attachment comment       */
  449.     TG} level;                /* trailing garbage         */
  450.  
  451.     *fn = *sz = *cmnt = '\0';            /* initialize return strings */
  452.     p   = tmp;
  453.     orig_offset = *off;
  454.  
  455.     level = LWS;                /* start at beginning */
  456.     while(*lp != NULL){
  457.  
  458.     if((c=(*lp)->text[*off]) == '\0'){    /* end of display line */
  459.         int lwsp = 0;
  460.  
  461.         while(isspace((unsigned char)(*lp)->text[lwsp]))
  462.           lwsp++;
  463.  
  464.         if(lwsp){                /* blat leading whitespace  */
  465.         *off -= lwsp;
  466.         for(j = 0; (*lp)->text[j] = (*lp)->text[j+lwsp]; j++)
  467.           ;
  468.         }
  469.  
  470.         while(*off && isspace((unsigned char)(*lp)->text[*off - 1]))
  471.           (*lp)->text[--(*off)] = '\0';    /* blat trailing whitespace */
  472.  
  473.         lprev = *lp;
  474.         if((*lp = (*lp)->next) != NULL)
  475.           c = (*lp)->text[*off = 0];    /* reset offset */
  476.     }
  477.  
  478.     if(c != '\0')
  479.       c_lookahead = (*lp)->text[*off + 1];
  480.  
  481.     switch(level){
  482.       case LWS:                /* skip leading white space */
  483.         if(isspace((unsigned char)c) || c == '\0'){
  484.         break;
  485.         } else if(c == ','){
  486.         c = (*lp)->text[*off] = ' ';    /* rub out stray comma */
  487.         break;
  488.         }
  489.         else if(!isdigit((unsigned char)c)){ /* add a number */
  490.         sprintf(number, "%d. ", *no);
  491.         *no = 0;            /* no previous number! */
  492.         sinserts((*lp == NULL) ? &lprev->text[*off]
  493.                            : &(*lp)->text[*off],
  494.                  0, number, j=strlen(number));
  495.         *off += j - 1;
  496.         rv = 1;
  497.         level = TAG;            /* interpret the name */
  498.         break;
  499.         }
  500.         level = NUMB;
  501.       case NUMB:                /* attachment number */
  502.         if(c == '\0' || c == ','){        /* got to end, no number yet */
  503.         *p = '\0';
  504.         sprintf(number, "%d. ", *no);
  505.         *no = 0;            /* no previous number! */
  506.         if(c == '\0')
  507.           *lp = lprev;            /* go back and look at prev */
  508.  
  509.         c = (*lp)->text[*off = orig_offset];    /* reset offset */
  510.         sinserts((*lp == NULL) ? &lprev->text[*off]
  511.                            : &(*lp)->text[*off],
  512.                  0, number, j=strlen(number));
  513.         *off += j - 1;
  514.         rv = 1;
  515.         p = tmp;
  516.         level = WSN;            /* what's next... */
  517.         break;
  518.         }
  519.         else if(c == '.' && isspace((unsigned char)c_lookahead)){
  520.                         /* finished grabbing number   */
  521.                         /* if not space is not number */
  522.         /*
  523.          * replace number if it's not right
  524.          */
  525.         *p = '\0';
  526.         sprintf(number, "%d", *no);    /* record the current...  */
  527.         *no = atoi(tmp);        /* and the old place in list */
  528.         if(strcmp(number, tmp)){
  529.             if(p-tmp > *off){        /* where to begin replacemnt */
  530.             j = (p-tmp) - *off;
  531.             sinserts((*lp)->text, *off, "", 0);
  532.             sinserts(&lprev->text[strlen(lprev->text)-j], j, 
  533.                  number, strlen(number));
  534.             *off = 0;
  535.             }
  536.             else{
  537.             j = (*off) - (p-tmp);
  538.             sinserts((*lp == NULL) ? &lprev->text[j] 
  539.                                : &(*lp)->text[j], 
  540.                  p-tmp , number, strlen(number));
  541.             *off += strlen(number) - (p-tmp);
  542.             }
  543.             rv = 1;
  544.         }
  545.  
  546.         p = tmp;
  547.         level = WSN;            /* what's next... */
  548.         }
  549.         else if(c < '0' || c > '9'){    /* Must be part of tag */
  550.         sprintf(number, "%d. ", *no);
  551.         sinserts((*lp == NULL) ? &lprev->text[(*off) - (p - tmp)]
  552.                            : &(*lp)->text[(*off) - (p - tmp)],
  553.                  0, number, j=strlen(number));
  554.         *off += j;
  555.         level = TAG;            /* interpret the name */
  556.         goto process_tag;    /* in case already past end of tag */
  557.         }
  558.         else
  559.           *p++ = c;
  560.  
  561.         break;
  562.  
  563.       case WSN:                /* blast whitespace */
  564.         if(isspace((unsigned char)c) || c == '\0'){
  565.         break;
  566.         }
  567.         else if(c == '['){            /* labeled attachment */
  568.         lbln++;
  569.         }
  570.         else if(c == ',' || c == ' '){
  571.         emlwrite("\007Attchmnt: '%c' not allowed in file name", 
  572.               (void *)(int)c);
  573.         rv = -1;
  574.         level = TG;            /* eat rest of garbage */
  575.         break;
  576.         }
  577.         level = TAG;
  578.       case TAG:                /* get and check filename */
  579.                         /* or labeled attachment  */
  580. process_tag:                    /* enclosed in []         */
  581.         if(c == '\0' || (!lbln
  582.                 && (isspace((unsigned char)c) || strchr(",(\"", c)))
  583.            || (lbln && c == ']')){
  584.         if(p != tmp){
  585.             *p = '\0';            /* got something */
  586.  
  587.             strcpy(fn, tmp);        /* store file name */
  588.             if(!lbln){            /* normal file attachment */
  589.             if(gmode&MDTREE)
  590.               compresspath(opertree, fn, NLINE);
  591.  
  592.             fixpath(fn, NLINE);
  593.             if((status=fexist(fn, "r", &l)) != FIOSUC){
  594.                 fioperr(status, fn);
  595.                 rv = -1;
  596.                 level = TG;        /* munch rest of garbage */
  597.                 break;
  598.             }
  599.  
  600.             if((gmode&MDTREE) && !in_oper_tree(fn)){
  601.                 emlwrite("\007Attachments allowed only from %s",
  602.                 (gmode&MDSCUR) ? "home directory" : opertree);
  603.                 rv = -1;
  604.                 level = TG;
  605.                 break;
  606.             }
  607.  
  608.             if(strcmp(fn, tmp)){     /* fn changed: display it */
  609.                 if(*off >=  p - tmp){    /* room for it? */
  610.                 sinserts((*lp == NULL)? 
  611.                      &lprev->text[(*off)-(p-tmp)] :
  612.                      &(*lp)->text[(*off)-(p-tmp)],
  613.                      p-tmp, fn, j=strlen(fn));
  614.                 *off += j - (p - tmp);    /* advance offset */
  615.                 rv = 1;
  616.                 }
  617.                 else{
  618.                 emlwrite("\007Attchmnt: Problem displaying real file path", NULL);
  619.                 }
  620.             }
  621.             }
  622.             else{            /* labelled attachment! */
  623.             /*
  624.              * should explain about labelled attachments:
  625.              * these are attachments that came into the composer
  626.              * with meaningless file names (up to caller of 
  627.              * composer to decide), for example, attachments
  628.              * being forwarded from another message.  here, we
  629.              * just make sure the size stays what was passed
  630.              * to us.  The user is SOL if they change the label
  631.              * since, as it is now, after changed, it will
  632.              * just get dropped from the list of what gets 
  633.              * passed back to the caller.
  634.              */
  635.             PATMT *tp;
  636.  
  637.             if(c != ']'){        /* legit label? */
  638.                 emlwrite("\007Attchmnt: Expected ']' after \"%s\"",
  639.                      fn);
  640.                 rv = -1;
  641.                 level = TG;
  642.                 break;
  643.             }
  644.             strcat(fn, "]");
  645.  
  646.             /*
  647.              * This is kind of cheating since otherwise
  648.              * ParseAttach doesn't know about the attachment
  649.              * struct.  OK if filename's not found as it will
  650.              * get taken care of later...
  651.              */
  652.             tp = Pmaster->attachments; /* caller check Pmaster! */
  653.             j = 0;
  654.             while(tp != NULL){
  655.                 if(++j == *no){
  656.                 lblsz = tp->size;
  657.                 break;
  658.                 }
  659.                 tp = tp->next;
  660.             }
  661.  
  662.             if(tp == NULL){
  663.                 emlwrite("\007Attchmnt: Unknown reference: %s",fn);
  664.                 lblsz =  "XXX";
  665.             }
  666.             }
  667.  
  668.             p = tmp;            /* reset p in tmp */
  669.             level = WST;
  670.         }
  671.  
  672.         if(!lbln && c == '(')        /* no space 'tween file, size*/
  673.           level = SIZE;
  674.         else if(c == '\0' || (!lbln && (c == ',' || c == '\"'))){
  675.             strcpy(sz, (lblsz) ? lblsz : prettysz(l));
  676.             sprintf(tmp, " (%s) %s", sz, (c == '\"') ? "" : "\"\"");
  677.             sinserts((*lp == NULL) ? &lprev->text[*off] 
  678.                                : &(*lp)->text[*off],
  679.                  0, tmp, j = strlen(tmp));
  680.             *off += j;
  681.             rv = 1;
  682.             level = (c == '\"') ? CMMNT : TG;/* cmnt or eat trash */
  683.         }
  684.         }
  685.         else if(!lbln && (c == ',' || c == ' ' || c == '[' || c == ']')){
  686.         emlwrite("\007Attchmnt: '%c' not allowed in file name",
  687.               (void *)(int)c);
  688.         rv = -1;            /* bad char in file name */
  689.         level = TG;            /* gobble garbage */
  690.         }
  691.         else
  692.           *p++ = c;                /* add char to name */
  693.  
  694.         break;
  695.  
  696.       case WST:                /* skip white space */
  697.         if(!isspace((unsigned char)c)){
  698.         /*
  699.          * whole attachment, comment or done! 
  700.          */
  701.         if(c == ',' || c == '\0' || c == '\"'){
  702.             strcpy(sz, (lblsz) ? lblsz : prettysz(l));
  703.             sprintf(tmp, " (%s) %s", sz, 
  704.                            (c == '\"') ? "" : "\"\"");
  705.             sinserts((*lp == NULL) ? &lprev->text[*off]
  706.                            : &(*lp)->text[*off],
  707.                  0, tmp, j = strlen(tmp));
  708.             *off += j;
  709.             rv = 1;
  710.             level = (c == '\"') ? CMMNT : TG;
  711.             lbln = 0;            /* reset flag */
  712.         }
  713.         else if(c == '('){        /* get the size */
  714.             level = SIZE;
  715.         }
  716.         else{
  717.             emlwrite("\007Attchmnt: Expected '(' or '\"' after %s",fn);
  718.             rv = -1;            /* bag it all */
  719.             level = TG;
  720.         }
  721.         }
  722.         break;
  723.  
  724.       case SIZE:                /* check size */
  725.         if(c == ')'){            /* finished grabbing size */
  726.         *p = '\0';
  727.         /*
  728.          * replace sizes if they don't match!
  729.          */
  730.         strcpy(sz, tmp);
  731.         if(strcmp(sz, (lblsz) ? lblsz : prettysz(l))){
  732.             strcpy(sz, (lblsz) ? lblsz : prettysz(l));
  733.             if(p-tmp > *off){        /* where to begin replacemnt */
  734.             j = (p-tmp) - *off;
  735.             sinserts((*lp)->text, *off, "", 0);
  736.             sinserts(&lprev->text[strlen(lprev->text)-j], j, 
  737.                  sz, strlen(sz));
  738.             *off = 0;
  739.             }
  740.             else{
  741.             j = (*off) - (p-tmp);
  742.             sinserts((*lp == NULL) ? &lprev->text[j]
  743.                                : &(*lp)->text[j],
  744.                  p-tmp , sz, strlen(sz));
  745.             *off += strlen(sz) - (p-tmp);
  746.             }
  747.             rv = 1;
  748.         }
  749.  
  750.         p = tmp;
  751.         level = SWS;            /* what's next... */
  752.         }
  753.         else if(c == '\0' || c == ','){
  754.         *p = '\0';
  755.         emlwrite("\007Attchmnt: Size field missing ')': \"%s\"", tmp);
  756.         rv = -1;
  757.         level = TG;
  758.         }
  759.         else
  760.           *p++ = c;
  761.  
  762.         break;
  763.  
  764.       case SWS:                /* skip white space */
  765.         if(!isspace((unsigned char)c)){
  766.         if(c == ','){            /* no description */
  767.             level = TG;            /* munch rest of garbage */
  768.             lbln = 0;            /* reset flag */
  769.         }
  770.         else if(c != '\"' && c != '\0'){
  771.             emlwrite("\007Attchmnt: Malformed comment, quotes required", NULL);
  772.             rv = -1;
  773.             level = TG;
  774.         }
  775.         else
  776.           level = CMMNT;
  777.         }
  778.         break;
  779.  
  780.       case CMMNT:                /* slurp up comment */
  781.         if(c == '\"' || c == '\0'){        /* got comment */
  782.         *p = '\0';            /* cap it off */
  783.         p = tmp;            /* reset p */
  784.         strcpy(cmnt,tmp);        /* copy the comment  */
  785.         if(c == '\0'){
  786.             emlwrite("\007Attchmnt: Closing quote required at end of comment", NULL);
  787.             rv = -1;
  788.         }
  789.         level = TG;            /* prepare for next one */
  790.         lbln = 0;            /* reset flag */
  791.         }
  792.         else
  793.           if(((*p++ = c) & 0x80) && !hibit++)
  794.         emlwrite(HIBIT_WARN, NULL);
  795.  
  796.         break;
  797.  
  798.       case TG:                /* get comma or final EOL */
  799.         if(!isspace((unsigned char)c)){
  800.         if(c != ',' && c != '\0'){
  801.             if(rv != -1)
  802.               emlwrite("\007Attchmnt: Comma must separate attachments", NULL);
  803.             rv = -1;
  804.         }
  805.         }
  806.         break;
  807.  
  808.       default:                /* something's very wrong */
  809.         emlwrite("\007Attchmnt: Weirdness in ParseAttach", NULL);
  810.         return(-1);                /* just give up */
  811.     }
  812.  
  813.     if(c == '\0')                /* we're done */
  814.       break;
  815.  
  816.     (*off)++;
  817.  
  818.     /*
  819.      * not in comment or label name? done. 
  820.      */
  821.     if(c == ',' && (level != CMMNT && !lbln))
  822.       break;                /* put offset past ',' */
  823.     }
  824.  
  825.     return(rv);
  826. }
  827.  
  828.  
  829.  
  830. /*
  831.  * NewAttach - given a filename (assumed to accessible) and comment, creat
  832.  */
  833. PATMT *NewAttach(f, l, c)
  834. char *f;
  835. long l;
  836. char *c;
  837. {
  838.     PATMT  *tp;
  839.  
  840.     if((tp=(PATMT *)malloc(sizeof(PATMT))) == NULL){
  841.     emlwrite("No memory to add attachment", NULL);
  842.     return(NULL);
  843.     }
  844.     else
  845.       memset(tp, 0, sizeof(PATMT));
  846.  
  847.     /* file and size malloc */
  848.     if((tp->filename = (char *)malloc(strlen(f)+1)) == NULL){
  849.     emlwrite("Can't malloc name for attachment", NULL);
  850.     free((char *) tp);
  851.     return(NULL);
  852.     }
  853.     strcpy(tp->filename, f);
  854.  
  855.     if(l > -1){
  856.     tp->size = (char *)malloc(sizeof(char)*(strlen(prettysz(l))+1));
  857.     if(tp->size == NULL){
  858.         emlwrite("Can't malloc size for attachment", NULL);
  859.         free((char *) tp->filename);
  860.         free((char *) tp);
  861.         return(NULL);
  862.     }
  863.     else
  864.       strcpy(tp->size, prettysz(l));
  865.     }
  866.  
  867.     /* description malloc */
  868.     if((tp->description = (char *)malloc(strlen(c)+1)) == NULL){
  869.     emlwrite("Can't malloc description for attachment", NULL);
  870.     free((char *) tp->size);
  871.     free((char *) tp->filename);
  872.     free((char *) tp);
  873.     return(NULL);
  874.     }
  875.     strcpy(tp->description, c);
  876.  
  877.     /* callback to show user the mime type that will be used for attachment */
  878.     if(Pmaster->mimetype  && (*Pmaster->mimetype)(f) > 0){
  879.     clearcursor();
  880.     mlerase();
  881.     (*Pmaster->showmsg)('x');
  882.     mpresf = 1;
  883.     }
  884.  
  885.     return(tp);
  886. }
  887.  
  888.  
  889.  
  890. /*
  891.  * AttachError - Sniff list of attachments, returning TRUE if there's
  892.  *               any sign of trouble...
  893.  */
  894. int
  895. AttachError()
  896. {
  897.     PATMT *ap;
  898.  
  899.     if(!Pmaster)
  900.       return(0);
  901.  
  902.     ap = Pmaster->attachments;
  903.     while(ap){
  904.     if((ap->flags) & A_ERR)
  905.       return(1);
  906.  
  907.     ap = ap->next;
  908.     }
  909.  
  910.     return(FALSE);
  911. }
  912.  
  913.  
  914.  
  915. void ZotAttach(p)
  916. PATMT *p;
  917. {
  918.     if(!p)
  919.       return;
  920.     if(p->description)
  921.       free((char *)p->description);
  922.     if(p->filename){
  923.     if(p->flags & A_TMP)
  924.       unlink(p->filename);
  925.  
  926.     free((char *)p->filename);
  927.     }
  928.     if(p->size)
  929.       free((char *)p->size);
  930.     if(p->id)
  931.       free((char *)p->id);
  932.     p->next = NULL;
  933. }
  934. #endif    /* ATTACHMENTS */
  935.  
  936.  
  937. /*
  938.  * intag - return TRUE if i is in a column that makes up an
  939.  *         attachment line number
  940.  */
  941. intag(s, i)
  942. char *s;
  943. int   i;
  944. {
  945.     char *p = s;
  946.     int n = 0;
  947.  
  948.     while(*p != '\0' && (p-s) < 5){        /* is there a tag? it */
  949.     if(n && *p == '.')            /* can't be more than 4 */
  950.       return(i <= p-s);                /* chars long! */
  951.  
  952.     if(*p < '0' || *p > '9')
  953.       break;
  954.     else
  955.       n = (n * 10) + (*p - '0');
  956.  
  957.     p++;
  958.     }
  959.  
  960.     return(FALSE);
  961. }
  962.  
  963.  
  964. /*
  965.  * prettysz - return pointer to string containing nice
  966.  */
  967. char *prettysz(l)
  968. long l;
  969. {
  970.     static char b[32];
  971.  
  972.     if(l < 1000)
  973.       sprintf(b, "%d  B", l);            /* xxx B */
  974.     else if(l < 10000)
  975.       sprintf(b, "%1.1f KB", (float)l/1000);    /* x.x KB */
  976.     else if(l < 1000000)
  977.       sprintf(b, "%d KB", l/1000);        /* xxx KB */
  978.     else if(l < 10000000)
  979.       sprintf(b, "%1.1f MB", (float)l/1000000); /* x.x MB */
  980.     else
  981.       sprintf(b, "%d MB", l/1000000);        /* xxx MB */
  982.     return(b);
  983. }
  984.  
  985.  
  986. /*
  987.  * sinserts - s insert into another string
  988.  */
  989. sinserts(ds, dl, ss, sl)
  990. char *ds, *ss;                    /* dest. and source strings */
  991. int  dl, sl;                    /* their lengths */
  992. {
  993.     char *dp, *edp;                /* pointers into dest. */
  994.     int  j;                    /* jump difference */
  995.  
  996.     if(sl >= dl){                /* source bigger than dest. */
  997.     dp = ds + dl;                /* shift dest. to make room */
  998.     if((edp = strchr(dp, '\0')) != NULL){
  999.         j = sl - dl;
  1000.  
  1001.         for( ;edp >= dp; edp--)
  1002.           edp[j] = *edp;
  1003.  
  1004.         while(sl--)
  1005.            *ds++ = *ss++;
  1006.     }
  1007.     else
  1008.       emlwrite("\007No end of line???", NULL);    /* can this happen? */
  1009.     }
  1010.     else{                    /* dest is longer, shrink it */
  1011.     j = dl - sl;                /* difference in lengths */
  1012.  
  1013.     while(sl--)                /* copy ss onto ds */
  1014.       *ds++ = *ss++;
  1015.  
  1016.     if(strlen(ds) > j){            /* shuffle the rest left */
  1017.         do
  1018.           *ds = ds[j];
  1019.         while(*ds++ != '\0');
  1020.     }
  1021.     else
  1022.       *ds = '\0';
  1023.     }
  1024. }
  1025.